import json
import platform
import sys
import time

from re_common.baselibrary.utils.basedir import BaseDir
from re_common.baselibrary.utils.basefile import BaseFile
from re_common.baselibrary.utils.baseip import BaseIp
from re_common.baselibrary.utils.basetime import BaseTime

from apps.crawler_platform.util.Mongodbhelper import MongoDBHelper
from apps.crawler_platform.util.sqlhelper import func_time, callback
from apps.sql_app.database import sets

QueryCheckDic = {
    "projection": "selector",
    "sort": "order_by",
    "limit": "num_to_return"
}


class SequoiadbHelper(object):
    sdb_utils = None

    def __init__(self):
        pass

    @classmethod
    @func_time(callback)
    async def find_one(cls, sdb_conn, filter, **kwargs):
        """
        mongo_conn是到表级别的连接
        filter 是查询条件
        *args, **kwargs 与find 保持一致
        find: https://pymongo.readthedocs.io/en/3.12.0/api/pymongo/collection.html#pymongo.collection.Collection.find
        max_time_ms（可选）： max_time_ms 的值可以指定为**kwargs 的一部分，例如：
        return: 返回单个文档的字典对象,如果没有找到文档 返回None
        """
        results = cls.sdb_utils.query(condition=filter, num_to_return=1, cl=sdb_conn)
        try:
            result = results.next()
        except:
            result = {}
        return result

    @classmethod
    @func_time(callback)
    async def find(cls, sdb_conn, filter, **kwargs):
        """
        mongo_conn是到表级别的连接
        filter 是查询条件
        *args, **kwargs 与find 保持一致
        find: https://motor.readthedocs.io/en/stable/api-asyncio/asyncio_motor_collection.html#motor.motor_asyncio.AsyncIOMotorCollection.find
        https://pymongo.readthedocs.io/en/3.12.0/api/pymongo/collection.html#pymongo.collection.Collection.find
        max_time_ms（可选）： max_time_ms 的值可以指定为**kwargs 的一部分，例如：
        return: motor.motor_asyncio.AsyncIOMotorCursor
        注意：注意find不需要await表达式，因为 find只创建一个 MotorCursor而不在服务器上执行任何操作。 MotorCursor方法，例如to_list() 执行实际操作。
        """
        newKwargs = {}
        for k, v in kwargs.items():
            if k not in QueryCheckDic.keys():
                raise RuntimeError('巨杉mongo查询参数不正确')
            newKwargs[QueryCheckDic[k]] = v
        results = cls.sdb_utils.query(condition=filter, cl=sdb_conn, **newKwargs)
        data_list = list()
        while 1:
            try:
                result = results.next()
                data_list.append(result)
            except:
                break
        return data_list

    @classmethod
    @func_time(callback)
    async def replace_one(cls, sdb_conn, filter, replacement, upsert=False, **kwargs):
        """
        replace_one会完全替换文档，只有主id不变，而不是融合文档
        mongo_conn: 是到表级别的连接
        filter: 是查询条件
        replacement: 替换成该文档
        该UPSERT选项可以使用，如果匹配的文件不存在，插入一个新的文件：
        https://motor.readthedocs.io/en/stable/api-asyncio/asyncio_motor_collection.html#motor.motor_asyncio.AsyncIOMotorCollection.replace_one
        return: pymongo.results.UpdateResult
        https://pymongo.readthedocs.io/en/3.12.0/api/pymongo/results.html#pymongo.results.UpdateResult
        提示: 巨杉兼容mongodb协议的updateOne/deleteOne 只能在单个分区、单个子表上执行。replace_one使用了updateOne
        """
        kwargs = {"condition": filter}
        result = cls.sdb_utils.replace_one(replacement, cl=sdb_conn, **kwargs)
        return result

    @classmethod
    @func_time(callback)
    async def insert_one(cls, sdb_conn, document, **kwargs):
        """
        mongo_conn: 是到表级别的连接
        document: 要插入的文档
        https://motor.readthedocs.io/en/stable/api-asyncio/asyncio_motor_collection.html#motor.motor_asyncio.AsyncIOMotorCollection.insert_one
        return: pymongo.results.InsertOneResult
        """
        result = cls.sdb_utils.insert(document, cl=sdb_conn)
        return result

    @classmethod
    @func_time(callback)
    async def delete_many(cls, sdb_conn, filter, **kwargs):
        """
        mongo_conn: 是到表级别的连接
        filter : 要删除的文档条件
        https://motor.readthedocs.io/en/stable/api-asyncio/asyncio_motor_collection.html?highlight=delete_many#motor.motor_asyncio.AsyncIOMotorCollection.delete_many
        return: pymongo.results.DeleteResult
        """
        kw = {"condition": filter}
        result = cls.sdb_utils.delete(cl=sdb_conn, **kw)
        return result

    @classmethod
    @func_time(callback)
    async def update_many(cls, sdb_conn, filter, update, upsert=False, **kwargs):
        """
        mongo_conn: 是到表级别的连接
        filter : 要更新的文档条件
        https://motor.readthedocs.io/en/stable/api-asyncio/asyncio_motor_collection.html?highlight=update#motor.motor_asyncio.AsyncIOMotorCollection.update_many
        return: pymongo.results.UpdateResult
        说明: 巨杉数据库无法使用update_many，且不会报错，但无法更新
        """
        kw = {"condition": filter}
        result = cls.sdb_utils.update(update, cl=sdb_conn, **kw)
        return result

    @classmethod
    @func_time(callback)
    async def replace_one_substitute(cls, sdb_conn, query, insert_doc, upsert=False, **kwargs):
        """
        client:连接对象

        由于巨杉数据库无法使用replace_one,这是一个替换方法
        为了保持方法的一致性 这个方法和 replace_one基本相同
        在这里 upsert参数不起作用
        """
        # kwargs = {"condition": delete_id}
        # SequoiadbUtils.delete(mongo_conn, **kwargs)
        # insert_doc.update(delete_id)
        # result = SequoiadbUtils.insert(mongo_conn, insert_doc)
        # return result
        kw = {"condition": query}
        result = cls.sdb_utils.replace_one(insert_doc, cl=sdb_conn, **kw)
        return result

    @classmethod
    @func_time(callback)
    async def save_gt_16M_to_file(cls, id_, save_dict):
        from settings import get_settings
        sets = get_settings()
        save_cnt = 0
        is_save_success = False
        exc = Exception("保存文件失败" + id_)
        while save_cnt < sets.MONGO_SAVE_TRY_CNT:
            try:
                save_path = BaseFile.get_new_path(sets.MONGO_BASE_DIR, sets.MONGO_SAVE_PATH)
                BaseDir.create_dir(save_path)
                save_path = BaseFile.get_new_path(save_path, "{}.big_json".format(id_))
                BaseFile.single_write_file(save_path, json.dumps(save_dict, ensure_ascii=False))
                if BaseFile.is_file_exists(save_path):
                    is_save_success = True
                    break
                save_cnt += 1
            except:
                save_cnt += 1
                exc = sys.exc_info()[0]

        if not is_save_success:
            raise exc
        return True, save_path

    @classmethod
    @func_time(callback)
    async def lob_insert(cls, sdb_conn, record: str, sdb_conn_msg, sdb_data_msg: dict = None):
        r_lob = cls.sdb_utils.lob_insert(record, cl=sdb_conn)
        if sdb_data_msg is not None:
            sdb_data_msg.update({"lob_id": str(r_lob)})
            result = cls.sdb_utils.save(sdb_data_msg, cl=sdb_conn_msg)
            sdb_data_msg.update({"meta_data_id": str(result)})
        return True, sdb_data_msg

    @classmethod
    @func_time(callback)
    async def lob_delete(cls, sdb_conn, sdb_conn_msg, sdb_data_msg: dict = None):
        """
        sdb_data_msg: 查询条件
        """
        if sdb_data_msg is not None:
            results = cls.sdb_utils.query_and_remove(cl=sdb_conn_msg, condition=sdb_data_msg)
            try:
                result = results.next()
            except:
                result = {}
            if result:
                result["_id"] = str(result["_id"])
                oid = result["lob_id"]
                r_lob = cls.sdb_utils.remove_lob(oid, cl=sdb_conn)
        else:
            return False, {"msg": "请给出查询连接"}
        return True, result

    @classmethod
    @func_time(callback)
    async def replace_lob(cls, sdb_conn, record: str, lob_id, condition, sdb_conn_msg):
        dicts = {}
        r_lob = cls.sdb_utils.replace_lob(record, lob_id, cl=sdb_conn)
        dicts.update({"lob_id": str(lob_id)})
        now = BaseTime().get_beijin_date_strins(format="%Y-%m-%d %H:%M:%S")
        result = cls.sdb_utils.update(rule={"$set": {"update_time": now}},
                                      condition=condition, cl=sdb_conn_msg)
        dicts.update({"save": str(result)})
        return True, dicts

    @classmethod
    @func_time(callback)
    async def query_lob(cls, sdb_conn, lob_id):
        result = cls.sdb_utils.lob_query(lob_id, cl=sdb_conn)
        return result


if "Windows" == platform.system():
    NOSQLHelper = MongoDBHelper
else:
    if BaseIp().GetLocalIPByPrefix("192.168") in sets.SDB_USE_IP:
        from re_common.baselibrary.tools.sequoiadb_utils import SequoiadbUtils

        SequoiadbHelper.sdb_utils = SequoiadbUtils()
        NOSQLHelper = SequoiadbHelper
    else:
        NOSQLHelper = MongoDBHelper
